//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Xml;
using C = config;

namespace DataLayerBuilder
{
    public partial class DataGen
    {
        private string file;
        private string outputFolder;

        string[] pathList = new string[] {
            @".",
            @"..",
            @"..\..",
        };

        public DataGen()
        {
        }

        public void GenerateScripts(string inputFile, string outFolder, bool dbtoo)
        {
            this.file = inputFile;
            this.outputFolder = outFolder;

            XmlSerializer configReader = new XmlSerializer(typeof(config.DataLayout));
            configReader.UnknownAttribute += new XmlAttributeEventHandler(configReader_UnknownAttribute);
            configReader.UnknownElement += new XmlElementEventHandler(configReader_UnknownElement);
            configReader.UnknownNode += new XmlNodeEventHandler(configReader_UnknownNode);
            configReader.UnreferencedObject += new UnreferencedObjectEventHandler(configReader_UnreferencedObject);
            C.DataLayout config = (C.DataLayout)configReader.Deserialize(File.OpenRead(ConfigFile));

            SystemDef sysInfo;
            GlobalSettingsDef globalSettings;
            SystemPolicyDef systemPolicy;
            List<ObjectDef> objects;
            List<RelationDef> relations;
            List<EnumDef> enums;

            ProcessLayout(config, out sysInfo, out systemPolicy, out globalSettings, out objects, out relations, out enums);

            GenerateObjects(sysInfo,globalSettings,systemPolicy, objects, relations, enums);

            if (!dbtoo)
            {
                Console.WriteLine("Skipping database generation!");
            }
            else
            {
                GenerateDataStore(objects, relations);
            }
        }

        void FatalError(string mask, params object[] parms)
        {
            Console.Error.WriteLine(string.Format("FATAL: {0}", mask), parms);
            throw new ApplicationException("Invalid schema: " + string.Format(mask, parms));
        }

        void configReader_UnreferencedObject(object sender, UnreferencedObjectEventArgs e)
        {
            FatalError("Unreferenced object @ {0} ({1})", e.UnreferencedId,
                e.UnreferencedObject == null ? "<null>" : e.UnreferencedObject.GetType().FullName);
        }

        void configReader_UnknownNode(object sender, XmlNodeEventArgs e)
        {
            FatalError("Unknown node @ {0},{1}: Name={2}, Type={3}, Text={4}", e.LineNumber, e.LinePosition,
                e.Name, e.NodeType.ToString(), e.Text);
        }

        void configReader_UnknownElement(object sender, XmlElementEventArgs e)
        {
            FatalError("Unknown element @ {0},{1}: Name={2}, Expected={3}, Text={4}", e.LineNumber, e.LinePosition,
                e.Element.Name, e.ExpectedElements, e.Element.OuterXml);
        }

        void configReader_UnknownAttribute(object sender, XmlAttributeEventArgs e)
        {
            FatalError("Unknown attribute @ {0},{1}: Name={2}, Expected={3}", e.LineNumber, e.LinePosition,
                e.Attr.Name, e.ExpectedAttributes);

        }

        void Log(int level, string msg, params string[] args)
        {
            StringBuilder msg2 = new StringBuilder();

            for (int i = 0; i < 4 * level; i++)
                msg2.Append(' ');

            msg2.AppendFormat("-- " + msg, args);

            Console.WriteLine(msg2.ToString());
        }

        string ConfigFile
        {
            get
            {
                foreach (string p in pathList)
                {
                    string fName = Path.Combine(p, file);

                    if (File.Exists(fName))
                        return fName;
                }

                throw new FileNotFoundException(string.Format("File '{0}' not found", file));
            }
        }
    }
}
